package graphics;
/*
 * Zombinoes: Peter Mosher, Michael Pitts
 * TCSS 491 - Computational Worlds
 * Project 1
 */

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.media.opengl.GL2;

/**
 * This is static library of graphics methods that can be used to generate complex
 * shapes.
 * @author Michael Pitts, Peter Mosher
 * @version Feb 2, 2012
 */
public final class GraphicsLib {
	
	/** Mathematical sin of 60 degrees. */
	private static final float SIN_60 = (float)Math.sin(Math.PI / 3);
	
	/**
	 * Gets an internal node that will display a tri-force set of triangles.
	 */
	public static InternalNode getTriForce() {
		final Collection<Node> nodes = new ArrayList<Node>();
		AbstractNode node = getEquilateralTriangle();
		node.setTranslate(0, SIN_60 * 2 / 3, 0);
		nodes.add(node);
		node = getEquilateralTriangle();
		node.setTranslate(-0.5f, -SIN_60 / 3, 0);
		nodes.add(node);
		node = getEquilateralTriangle();
		node.setTranslate(0.5f, -SIN_60 / 3, 0);
		nodes.add(node);
		final InternalNode top_node = new InternalNode(nodes);
		top_node.setTranslate(0, -0.25f, 0);
		return top_node;
	}
	
	/**
	 * Gets a leaf node that displays an equilateral triangle.
	 */
	public static AbstractNode getEquilateralTriangle() {
		return new LeafNode() {
			private final float[] my_colors = {1, .8f, 0};
			@Override protected void render(final GL2 the_gl) {
				the_gl.glBegin(GL2.GL_TRIANGLES);
				the_gl.glColor3fv(my_colors, 0);
				the_gl.glVertex3f(-.5f, -SIN_60 / 3, 0);
				the_gl.glVertex3f(0, SIN_60 * 2 / 3, 0);
				the_gl.glVertex3f(.5f, -SIN_60 / 3, 0);
				the_gl.glEnd();
			}
			
			@Override public void update(final Object... the_data) {
				my_colors[0] = 1 - my_colors[0];
				my_colors[1] = 1 - my_colors[1];
				my_colors[2] = 1 - my_colors[2];
			}
		};
	}

	/**
	 * Gets the root node of a populated graphics Tree.
	 */
	public static InternalNode getRoot() {
		return new InternalNode(getOtherNodes());
	}

	/**
	 * Gets the collection of sub nodes for the graphics tree.
	 */
	private static Collection<Node> getOtherNodes() {
		final Collection<Node> nodes = new ArrayList<Node>();
		nodes.add(getTriForce());
		final Node circle = getCircle(90);
		circle.setScale(0.25f, 0.25f, 1);
		circle.setTranslate(0, -0.25f, 0);
		nodes.add(circle);
		final Node square = getSquare();
		square.setScale(0.25f, 0.25f, 1);
		square.setTranslate(0.75f, 0.5f, 0f);
		nodes.add(square);
		return nodes;
	}

	/** Tracks which circles have been pre-mapped. */
	private static final Map<Integer, Boolean> my_slices = new HashMap<Integer, Boolean>();
	
	/**
	 * Gets a new circle node, constructed with the given number of slices.
	 */
	public static Node getCircle(final int the_slices) {
		return new LeafNode() {
			private final  float[] my_colors = {1f, 1f, 1f};
			@Override protected void render(final GL2 the_gl) {
				the_gl.glColor3fv(my_colors, 0);
				if (!my_slices.containsKey(the_slices)) {
					my_slices.put(the_slices, true);
					the_gl.glNewList(the_slices, GL2.GL_COMPILE_AND_EXECUTE);
					generateCircle(the_gl, the_slices);
					the_gl.glEndList();
					
				}
				the_gl.glCallList(the_slices);
			}
			
			@Override public void update(final Object... the_data) {
				if (my_colors[0] == 1) {
					my_colors[0] = 0.25f;
					my_colors[1] = 0.25f;
					my_colors[2] = 0.25f;
				} else {
					my_colors[0] = 1f;
					my_colors[1] = 1f;
					my_colors[2] = 1f;
				}
			}
		};
	}
	
	/**
	 * Generates a circle with the given number of slices.
	 * @param the_gl is the graphics object.
	 * @param the_slices is the number of slices that this circle will have.
	 */
	private static void generateCircle(final GL2 the_gl, final int the_slices) {
		the_gl.glBegin(GL2.GL_TRIANGLE_FAN);
		the_gl.glVertex3f(0, 0, 0);
		for (int i = 0; i <= the_slices; i++) {
			the_gl.glVertex3d(Math.cos(i * Math.PI * 2 / the_slices), 
					Math.sin(i * Math.PI * 2 / the_slices), 0);
		}
		the_gl.glEnd();
	}
	
	public static Node getSquare() {
		return new LeafNode() {
			final float[] my_colors = {0.5f, .8f, 0.5f};
			@Override protected void render(final GL2 the_gl) {
				the_gl.glBegin(GL2.GL_TRIANGLE_FAN);
				the_gl.glColor3fv(my_colors, 0);
				the_gl.glVertex3f(-1, -1, 0);
				the_gl.glVertex3f(-1, 1, 0);
				the_gl.glVertex3f(1, 1, 0);
				the_gl.glVertex3f(1, -1, 0);				
				the_gl.glEnd();
			}
			
			@Override public void update(final Object... the_data) {
				my_colors[0] = 1 - my_colors[0];
				my_colors[1] = 1 - my_colors[1];
				my_colors[2] = 1 - my_colors[2];
			}
		};
	}
	
	/** Gets a new Hexagon. */
	public static Node getHexagon() {
		final Node hexagon = getCircle(6);
		hexagon.setScale(0.25f, 0.25f, 1);
		hexagon.setTranslate(0, -0.25f, 0);
		return hexagon;
	}

	public static Node getTriangle(final float[] the_points, final float r, 
			final float g, final float b) {
		final float[] center = {(the_points[0]+the_points[2]+the_points[4])/3, 
				(the_points[1]+the_points[3]+the_points[5])/3};
		final Node temp =  new LeafNode() {
			private final float[] my_colors = {r, g, b};
			@Override protected void render(final GL2 the_gl) {
				the_gl.glBegin(GL2.GL_TRIANGLES);
				the_gl.glColor3fv(my_colors, 0);
//				the_gl.glVertex3f(the_points[0]-center[0], 
//						the_points[1]-center[1], 0);
//				the_gl.glVertex3f(the_points[2]-center[0], 
//						the_points[3]-center[1], 0);
//				the_gl.glVertex3f(the_points[4]-center[0], 
//						the_points[5]-center[1], 0);
				the_gl.glVertex3f(the_points[0], 
						the_points[1], 0);
				the_gl.glVertex3f(the_points[2], 
						the_points[3], 0);
				the_gl.glVertex3f(the_points[4], 
						the_points[5], 0);
				the_gl.glEnd();
			}
			
			@Override public void update(final Object... the_data) {
				// doesn't update
			}
		};
		//temp.setTranslate(center[0], center[1], 0);
		return temp;
	}
}
